//Copyright (C) 2012 Chirila Alexandru
//This file is part of Mirror FTP
//
//Mirror FTP is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//Mirror FTP is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with Mirror FTP.  If not, see <http://www.gnu.org/licenses/>.

#include "conn_dta.h"

conn_dta::conn_dta(){
	stop = 0;
}
conn_dta::~conn_dta(){}

int conn_dta::closeC(){
	///Close socket used to communicate with server.

	close(sockfd);
	return 0;
}

int conn_dta::establish_active(struct hostent server){
	///Establishes an active data connection by binding a socket and listening on it.
	///Succeeded by a PORT command.

	portno=0;
	socklen_t channellen;

	sockfd = socket(server.h_addrtype, SOCK_STREAM, 0); //create socket
	serv_addr.sin_family = server.h_addrtype;			//get server family from resolved host
	serv_addr.sin_addr.s_addr = htonl (INADDR_ANY);
	serv_addr.sin_port = htons(portno);					//get portno

	if (bind(sockfd,(struct sockaddr *) &serv_addr,sizeof (serv_addr)) < 0){	//bind
		perror ("Eroare la bind().\n");
		return errno;
	}
	if (listen(sockfd,10)<0){
		perror ("Eroare la listen().\n");
		return errno;
	}

	channellen=sizeof(serv_addr);
	getsockname(sockfd, (struct sockaddr*) &serv_addr, &channellen);
	portno = htons(serv_addr.sin_port);		//save port number to send later to server
	return 0;
}

int conn_dta::establish_passive(string p){
	///Establishes a passive connection by connecting to an ip and port specified by the server.
	///Preceded by a PASV command

	string buffer;
	const char *temp=p.c_str();
	char *addr=strchr((char *)temp,'(' );
	char *token;
	const char delimiters[]= ",()";

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	serv_addr.sin_family = AF_INET;

	token = strtok(addr,delimiters);
	buffer=buffer+string(token)+".";

	token = strtok(NULL,delimiters);
	buffer=buffer+string(token)+".";

	token = strtok(NULL,delimiters);
	buffer=buffer+string(token)+".";

	token = strtok(NULL,delimiters);
	buffer=buffer+string(token);

	inet_aton(buffer.c_str(),&serv_addr.sin_addr);

	token = strtok(NULL,delimiters);
	portno=atoi(token)*256;

	token = strtok(NULL,delimiters);
	portno+=atoi(token);
	serv_addr.sin_port = htons(portno);

	if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0){	//establish connection
		perror("ERROR connecting\n");
		return errno;
	}
	return 0;
}


int conn_dta::listen_active(model *m,long long size,string path,char data_type){
	///Listen to the active connection and awaits data. Preceded by a PORT command.
	///See socket_read for arguments info.

	struct sockaddr_in from;
	int client;
	socklen_t length = sizeof (from);

	client = accept (sockfd, (struct sockaddr *) &from, &length);
	if (client < 0){
		perror ("Eroare la accept().\n");
		return errno;
	}
	return socket_read(m,size,client,path,data_type);
}

int conn_dta::listen_passive(model *m,long long size,string path,char data_type){
	///Listen to the active connection and awaits data. Preceded by connection establishing.
	///See socket_read for arguments info.

	return socket_read(m,size,sockfd,path,data_type);
}

void *conn_dta::timer(void *arg){
	///Time the download operation.
	///Argument is pointer to a long long variable in which the current recorded time is kept.

	long long *i=(long long*) arg;
	while(1){
		sleep(1);
		*i=*i+1;
	}
	return NULL;
}

int conn_dta::socket_read(model *m,long long size,int sock,string path,char data_type){
	///Reads from socket and write to file.
	///\param m model used to notify the view.
	///\param size specifies the size of the downloaded file, so time calculations can be computed
	///\param sock is the socket from which it reads
	///\param path is the place where the file is saved
	///\param data_type the type of data being received : \n 'A' - Ascii \n 'I' - Binary (Image) \n if not sure leave Binary mode
	///\return 0 in case of succes
	///\return -1 in case of a canceled operation

	fstream file;
	stop=0;

	char temp;
	int ret,len,x;
	long long speed=0,count=0,time=0,eta=0,old_t=0;

	x=size/100;

	switch (data_type){
	case 'a':
	case 'A':	//ASCII data
		file.open(path.c_str(),ios::out);
		break;
	case 'i':
	case 'I':	//BINARY data
		file.open(path.c_str(),ios::binary | ios::out);
		break;
	}
	len=0;
	m->notify();
	pthread_create(&thread,NULL,&conn_dta::timer,&time);
	do{
		if (stop==1)
			break;
		ret=read (sock, &temp, sizeof(char));
		if (ret < 0){
			perror ("Eroare la read().\n");
			return -2;
		}
		else{
			if (((temp!='\r') && (ret!=0)) || (data_type=='i') || (data_type=='I')){
				file.put(temp);
				if (len==x){
					if (time!=0){
						if (time!=old_t){
							old_t=time;
							speed=count/(old_t*1024);
							eta=(size-count)/(speed*1024);
						}
					}
					else{
						speed=0;
						eta=0;
					}
					m->notify(old_t,speed,eta);
					len=0;
				}
			}
		}
		len++;count++;
	}while(ret!=0);

	pthread_cancel(thread);

	file.close();
	close(sock);

	if (stop!=1){
		m->notify(-1,0,0);
		return 0;
	}
	else{
		return -1;
	}
}



